package com.supreme.commonboot.aspect;

import com.alibaba.fastjson.JSONObject;
import com.supreme.common.constant.ConsoleColors;
import com.supreme.commonboot.annotation.ControllerAopMatch;
import com.supreme.commonboot.utils.AjaxResp;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
import org.springframework.util.StopWatch;

/**
 * 定义Aspect切面拦截controller
 */
@Slf4j
@Aspect
@Component
@ControllerAopMatch(value = "ctrlAop")
public class ControllerAOP {

    @Pointcut(value = "execution(public * com.supreme..controller..*.*(..))")
    public void PointCut() {
    }

    /**
     * step1
     * proceedingJoinPoint.proceed() 之前调用: 请求之前处理
     * proceedingJoinPoint.proceed() 之后调用: 请求之后处理
     * proceedingJoinPoint.proceed() 异常通过声明抛出: 框架处理/全局异常处理
     *
     * 以下自定义处理异常
     * try {
     *     object = proceedingJoinPoint.proceed();
     * } catch (Throwable throwable) {
     *     throwable.printStackTrace();
     * }
     *
     * @param proceedingJoinPoint proceedingJoinPoint
     * @return Object
     */
    @Around("PointCut()")
    public Object aroundMethod(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        Signature signature = proceedingJoinPoint.getSignature();

        ConsoleColors.printf(ConsoleColors.RED_BOLD_BRIGHT, "[ AOP Around Before ] = %s", signature.toShortString());
        String methodName = signature.getName();
        StopWatch stopWatch = new StopWatch(methodName);

        stopWatch.start();
        Object proceed = proceedingJoinPoint.proceed();
        stopWatch.stop();

        ConsoleColors.println(ConsoleColors.RED_BOLD_BRIGHT, "[ AOP Around After ]");

        if (proceed instanceof AjaxResp) {
            double totalTimeNanos = stopWatch.getTotalTimeNanos();
            double totalSeconds = totalTimeNanos / 1000000000;
            double totalMillis = totalTimeNanos / 1000000;

            AjaxResp ajaxResp = (AjaxResp) proceed;
            JSONObject consumingJson = new JSONObject();
            consumingJson.put("seconds", totalSeconds);
            consumingJson.put("millis", totalMillis);
            consumingJson.put("nanos", totalTimeNanos);
            ajaxResp.put("consuming", consumingJson);
            return ajaxResp;
        }

        return proceed;
    }

    /**
     * step2
     * @param joinPoint: JoinPoint
     */
    @Before("PointCut()")
    public void beforeMethod(JoinPoint joinPoint) {
        Signature signature = joinPoint.getSignature();
        ConsoleColors.printf(ConsoleColors.GREEN_BOLD_BRIGHT, "[ AOP Before ] = %s", signature.toShortString());
    }

    /**
     * step3
     * @param joinPoint: JoinPoint
     * @param result: Object
     */
    @AfterReturning(value = "PointCut()", returning = "result")
    public void afterReturningMethod(JoinPoint joinPoint, Object result) {
        Signature signature = joinPoint.getSignature();
        ConsoleColors.printf(ConsoleColors.BLUE_BOLD_BRIGHT, "[ AOP AfterReturning ] = %s result = %s", signature.toShortString(), result.toString());
    }

    /**
     * step4
     * @param joinPoint: JoinPoint
     */
    @After("PointCut()")
    public void afterMethod(JoinPoint joinPoint) {
        Signature signature = joinPoint.getSignature();
        ConsoleColors.printf(ConsoleColors.CYAN_BOLD_BRIGHT, "[ AOP After ] = %s", signature.toShortString());
    }

    @AfterThrowing(value = "PointCut()", throwing = "exception")
    public void afterPointCutMethod(JoinPoint joinPoint, Exception exception) {
        Signature signature = joinPoint.getSignature();
        ConsoleColors.printf(ConsoleColors.YELLOW_BOLD_BRIGHT, "[ AOP AfterThrowing ] = %s exception = %s", signature.toShortString(), exception.getMessage());
    }
}
